using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

using System.Drawing;
using System.Drawing.Drawing2D;
using System.ComponentModel;
using System.Xml;

using System.IO;
using Athena.Core;

namespace Athena.Objects
{
    [Serializable()]
    public class ModuleGlyph : ContainerGlyph
    {
        /*
        [Serializable()]
        public class LinkGlyph : IGlyph
        {
            BezierConnector _Connector;
            public BezierConnector Connector
            {
                get { return _Connector; }
                set { _Connector = value; }
            }
            IGlyph _Target;
            public IGlyph Target
            {
                get { return _Target; }
                set 
                { 
                    _Target = value;
                    _Target.AddAlias(this);
                }
            }

            public enum DecoratorStyle
            {
                Circle,
                Ellipse,
                Rectangle
            }

            protected DecoratorStyle _Style = DecoratorStyle.Circle;
            public DecoratorStyle Style
            {
                get { return _Style; }
                set { _Style = value; }
            }
            
            public override IDrawable Parent
            {
                get
                {
                    return _LinkedSource;
                }
                set
                {                    
                }
            }
            public LinkGlyph(ModuleGlyph module, IGlyph target, bool input) : base()
            {
                LinkedSource = module;
                _Parent = module;
                Dimensions = new SizeF(10, 10);

                _Style = DecoratorStyle.Rectangle;

                _oFillColorStart = _oFillColorEnd = Color.Blue;
                _oEdgeColor = Color.Blue;

                Target = target;
                
                if (Original == null) return;

                if (input)
                    _Connector = new BezierConnector(this, Original);
                else
                    _Connector = new BezierConnector(Original, this);

                _Connector.Item.Remove(); //very important -- we don't want this reaction in the model
                _Connector.Item = null;

                _Connector.FixedReaction = true;

                List<Connector> connections = new List<Connector>();

                connections.AddRange(target.InputConnections);

                int numInputs = connections.Count;

                connections.AddRange(target.OutputConnections);

                List<IDrawable> drawables = new List<IDrawable>();
                drawables.Add(target);

                int i = 0;
                foreach (Connector c in connections)
                {
                    if (c == _Connector || c.Parent == module) continue;
                    
                    c.SubstituteNode(target, this);

                    if (i < numInputs)
                        drawables.AddRange(c.Inputs);
                    else
                        drawables.AddRange(c.Outputs);

                    ++i;
                }

                _Location = Utility.CenterPoint(drawables);

                DoneModifying();
                //_Connector.StraightLines = true;
                _Connector.Parent = module;                
                _Connector.FillColorEnd = _Connector.FillColorStart = Color.Firebrick;
                foreach (Connector c in connections) 
                    if (c.Parent != module)
                        c.MakePretty();
                _Connector.MakePretty();
            }

            public override void AddInputConnection(Connector connector)
            {
                bool inside = false;
                if (_Connector != null && connector != _Connector)
                {
                    foreach (IDrawable id in connector.InputsAndOutputs)
                    {
                        if (id != this && id.Parent == Parent)
                        {
                            inside = true;
                            break;
                        }
                    }
                }
                if (inside)
                    connector.RemoveNode(this);
                else
                    base.AddInputConnection(connector);
            }
            
            public override void AddOutputConnection(Connector connector)
            {
                bool inside = false;
                if (_Connector != null && connector != _Connector)
                {
                    foreach (IDrawable id in connector.InputsAndOutputs)
                    {
                        if (id != this && id.Parent == Parent)
                        {
                            inside = true;
                            break;
                        }
                    }
                }
                if (inside)
                    connector.RemoveNode(this);
                else
                    base.AddOutputConnection(connector);
            }
            public override void Remove()
            {
                base.Remove();
                _Connector.Remove();
            }
            public override object Clone()
            {
                return new LinkGlyph(Parent as ModuleGlyph, Target, true);
            }
            public override Bitmap GetPreview(int width, int height)
            {
                return null;
            }

            public override void RenderGlyph(Graphics g)
            {
                
            }

            public override void Draw(Graphics g)
            {                
                if (!Visible) return;

                Pen edgePen = new Pen(EdgeColor, EdgeThickness);
                Brush edgeBrush = new SolidBrush(EdgeColor);
                //Brush backgroundBrush = new SolidBrush(Program.Model.Background.StartColor);
                Brush backgroundBrush;

                if (FillColorEnd == FillColorStart)
                    backgroundBrush = new SolidBrush(FillColorEnd);
                else
                    backgroundBrush = new LinearGradientBrush(Location, new PointF(Location.X + Dimensions.Width, Location.Y + Dimensions.Height), _oFillColorStart, _oFillColorEnd);

                switch (Style)
                {
                    case DecoratorStyle.Circle:
                        g.FillEllipse(backgroundBrush, Bounds);
                        g.DrawEllipse(edgePen, Bounds);
                        break;
                    case DecoratorStyle.Ellipse:
                        g.FillEllipse(backgroundBrush, Bounds);
                        g.DrawEllipse(edgePen, Bounds);
                        break;
                    case DecoratorStyle.Rectangle:
                        g.FillRectangle(backgroundBrush, Bounds);
                        g.DrawRectangle(edgePen, Bounds.X, Bounds.Y, Bounds.Width, Bounds.Height);
                        break;
                    default:
                        break;
                }

                DrawLabel(g);

                if (_IsSelected)
                {
                    RectangleF oBounds = Bounds;

                    oBounds.Inflate(2.5F, 2.5F);

                    g.DrawLine(Pens.Red, oBounds.Location.X + 3, oBounds.Location.Y, oBounds.Location.X + oBounds.Size.Width - 3, oBounds.Location.Y);
                    g.DrawLine(Pens.Red, oBounds.Location.X + 3, oBounds.Location.Y + oBounds.Height, oBounds.Location.X + oBounds.Width - 3, oBounds.Location.Y + oBounds.Height);
                    g.DrawLine(Pens.Red, oBounds.Location.X, oBounds.Location.Y + 3, oBounds.Location.X, oBounds.Location.Y + oBounds.Height - 3);
                    g.DrawLine(Pens.Red, oBounds.Location.X + oBounds.Width, oBounds.Location.Y + 3, oBounds.Location.X + oBounds.Width, oBounds.Location.Y + oBounds.Height - 3);

                    g.DrawEllipse(Pens.Red, oBounds.Left - 3, oBounds.Top - 3, 6, 6);
                    g.DrawEllipse(Pens.Red, oBounds.Right - 3, oBounds.Top - 3, 6, 6);
                    g.DrawEllipse(Pens.Red, oBounds.Left - 3, oBounds.Bottom - 3, 6, 6);
                    g.DrawEllipse(Pens.Red, oBounds.Right - 3, oBounds.Bottom - 3, 6, 6);
                }

            }
            public override void MoveTo(PointF value)
            {
                if (Parent == null)
                    _Location = value;
                else
                {
                    PointF oTemp = new PointF(value.X + _Dimensions.Width / 2f, value.Y + _Dimensions.Height / 2f);
                    float fDistance = 0f;//Math.Min(Dimensions.Height, Dimensions.Width) / 2f;
                    PointF oCenter = Utility.GetPointOnEdge(Parent.Bounds, oTemp, fDistance);
                    _Location = new PointF(oCenter.X - _Dimensions.Width / 2f, oCenter.Y - _Dimensions.Height / 2f);
                }
            }
            public override void DoneModifying()
            {
                PointF oTemp = Center;
                float fDistance = 0f;//Math.Min(Dimensions.Height, Dimensions.Width) / 2f;
                PointF oCenter = Utility.GetPointOnEdge(Parent.Bounds, oTemp, fDistance);
                _Location = new PointF(oCenter.X - Dimensions.Width / 2f, oCenter.Y - Dimensions.Height / 2f);

                base.DoneModifying();
            }
        }
        */
        //IDrawable _Left, _Right;        

        public override object Clone()
        {
            return new ModuleGlyph(this);
        }

        public ModuleGlyph(ModuleGlyph copy) : base(copy)
        {
            Item = new Module("module", "Module_0");
            for (int i = 0; i < _Drawables.Count; ++i)
            {
                ((Module)_Item).AddItem(_Drawables[i].Item);   //very important -- add the items to the part
            }
        }
        /*
        /// <summary>
        /// Get the left most item on the DNA strand in the module
        /// </summary>
        public PartGlyph LeftMostPart
        {
            get
            {
                if (Item is Module)
                {
                    Part part = ((Module)Item).GetLeftMostPart();
                    if (part != null)
                        return part.Drawable as PartGlyph;
                    else
                        return null;
                }
                else
                    return null;
            }
        }

        /// <summary>
        /// Get the right most item on the DNA strand in the module
        /// </summary>
        public PartGlyph RightMostPart
        {
            get
            {
                if (Item is Module)
                {
                    Part part = ((Module)Item).GetRightMostPart();
                    if (part != null)
                        return part.Drawable as PartGlyph;
                    else
                        return null;
                }
                else
                    return null;
            }
        }
        
        /// <summary>
        /// Get the Part or Module to the left of this module
        /// </summary>
        public IDrawable Left
        {
            get
            {
                return _Left;
            }
            set
            {
                if (value == this || (value != null && (value == _Right || value == _Left))) return;
                if (IsAlias || _Item == null || !(_Item is Module)) return;

                if (value != null && value.Parent != null)
                    while (value.Parent != Parent && value.Parent.Item is Module)
                        value = value.Parent;

                Module module = (Module)_Item;
                if (value == null)
                {
                    Part leftmost = module.GetLeftMostPart();
                    if (leftmost != null)
                    {
                        leftmost.Left = null;
                    }
                    if (_Left is ModuleGlyph)
                    {
                        ((ModuleGlyph)_Left)._Right = null;
                    }
                    _Left = null;
                    return;
                }
                if (value is PartGlyph)
                {
                    Part part = value.Item as Part;
                    Part leftmost = module.GetLeftMostPart();
                    if (leftmost != null)
                    {
                        leftmost.Left = part;
                    }
                    _Left = value;
                    return;
                }
                if (value is ModuleGlyph)
                {
                    Module module2 = value.Item as Module;
                    if (module2 != null && module2.IsPart)
                    {
                        Part rightmost = module2.GetRightMostPart();
                        Part leftmost = module.GetLeftMostPart();
                        if (leftmost != null)
                        {
                            leftmost.Left = rightmost;
                        }
                        ((ModuleGlyph)value)._Right = this;
                        _Left = value;
                    }
                    else
                        _Left = null;
                }
            }
        }
        public IDrawable Right
        {
            get
            {
                if (_Item == null || !(_Item is Module)) return null;
                return _Right;
            }
            set
            {
                if (value == this || (value != null && (value == _Right || value == _Left))) return;
                if (IsAlias || _Item == null || !(_Item is Module)) return;

                if (value != null && value.Parent != null)
                    while (value.Parent != Parent && value.Parent.Item is Module)
                        value = value.Parent;

                Module module = (Module)_Item;
                if (value == null)
                {
                    Part rightmost = module.GetRightMostPart();
                    if (rightmost != null)
                    {
                        rightmost.Right = null;
                    }
                    if (_Right is ModuleGlyph)
                    {
                        ((ModuleGlyph)_Right)._Left = null;
                    }
                    _Right = null;
                    return;
                }
                if (value is PartGlyph)
                {
                    Part part = value.Item as Part;
                    Part rightmost = module.GetRightMostPart();
                    if (rightmost != null)
                    {
                        rightmost.Right = part;
                    }
                    _Right = value;
                    return;
                }
                if (value is ModuleGlyph)
                {
                    Module module2 = value.Item as Module;
                    if (module2 != null && module2.IsPart)
                    {
                        Part rightmost = module.GetRightMostPart();
                        Part leftmost = module2.GetLeftMostPart();
                        if (rightmost != null)
                        {
                            rightmost.Right = leftmost;
                        }
                        ((ModuleGlyph)value)._Left = this;
                        _Right = value;
                    }
                    else
                        _Right = null;
                }
            }
        }


        public virtual List<IDrawable> ConnectedParts
        {
            get
            {
                List<IDrawable> list = new List<IDrawable>();
                IDrawable left = this, part = this;
                while (left != null && ((left is PartGlyph) || (left is ModuleGlyph)))
                {
                    part = left;
                    if (left is PartGlyph) left = ((PartGlyph)left).Left;
                    if (left is ModuleGlyph) left = ((ModuleGlyph)left).Left;
                }

                IDrawable right = part;

                while (right != null)
                {
                    list.Add(right);
                    if (right is PartGlyph) right = ((PartGlyph)right).Right;
                    if (right is ModuleGlyph) right = ((ModuleGlyph)right).Right;
                }
                return list;
            }
        }
        */
        
        /// <summary>
        /// Initializes a new instance of the PartGlyph class.
        /// </summary>
        /// <param name="location"></param>
        public ModuleGlyph(PointF location) : base(location)
        {
            TextH = HorizontalAlignment.Center;
            TextV = VerticalAlignment.BelowBottom;
            _oFillColorStart = Color.AliceBlue;
            _oFillColorEnd = Color.DeepSkyBlue;
            _oEdgeColor = Color.Black;
            Shape = SHAPE.Box;
            _dEdgeThickness = 7f;
            _oSelectThickness = 5f;
            Item = new Module("module", "M0");
        }

        public ModuleGlyph() : base()
        {
            _oFillColorStart = Color.AliceBlue;
            _oFillColorEnd = Color.DeepSkyBlue;
            _oEdgeColor = Color.Black;
            Shape = SHAPE.Box;
            _dEdgeThickness = 7f;
            _oSelectThickness = 5f;
            Item =new Module("module","M0");
        }

        public override bool HasPreview
        {
            get { return false; }
        }
/*
        public override void AddItem(IDrawable item)
        {
            if (item == null || IsAlias || _Drawables.Contains(item)) return;
            
            base.AddItem(item);
            if (item is PartGlyph)
            {
                PartGlyph partglyph = (PartGlyph)item;

                if (partglyph.Left != null && !IsParentOf(partglyph.Left))
                {
                    Left = partglyph.Left;
                    PartGlyph rightmost = RightMostPart;
                    if (rightmost != null && rightmost.Right != _Right)
                        Right = rightmost.Right;
                }
                else
                if (partglyph.Right != null && !IsParentOf(partglyph.Right))
                {
                    Right = partglyph.Right;
                    PartGlyph leftmost = RightMostPart;
                    if (leftmost != null && leftmost.Left != _Left)
                        Left = leftmost.Left;
                }

                if (partglyph == LeftMostPart)
                {
                    if (Left != null && Left != partglyph.Left)
                    {
                        Module left = Left.Item as Module;
                        if (left != null && left.IsPart)
                        {
                            Part part = left.GetRightMostPart();
                            if (part != null)
                                part.Right = item.Item as Part;
                        }
                    }
                }
                else
                    if (partglyph == RightMostPart)
                    {
                        if (Right != null && Right != partglyph.Right)
                        {
                            Module right = Right.Item as Module;
                            if (right != null && right.IsPart)
                            {
                                Part part = right.GetLeftMostPart();
                                if (part != null)
                                    part.Left = item.Item as Part;
                            }
                        }
                    }
                    else
                    {
                        PartGlyph leftmost = LeftMostPart as PartGlyph;
                        if (leftmost != null && item.Location.X < LeftMostPart.Location.X)
                        {
                            if (!Bounds.Contains(partglyph.Bounds))
                            {
                                partglyph.LeftMost.Left = leftmost.Left;
                                leftmost.Left = partglyph;
                            }
                        }
                        else
                        {
                            PartGlyph rightmost = RightMostPart as PartGlyph;
                            if (rightmost != null && item.Location.X > RightMostPart.Location.X)
                            {
                                if (!Bounds.Contains(partglyph.Bounds))
                                {
                                    partglyph.RightMost.Right = rightmost.Right;
                                    rightmost.Right = partglyph;
                                }
                            }
                        }

                    }
                ((PartGlyph)item).UpdateLocation();
            }
            if (item is ModuleGlyph)
            {
                ModuleGlyph module = ((ModuleGlyph)item);
                if (module.Left != null && !Bounds.Contains(module.Left.Bounds))
                    Left = module.Left;
                if (module.Right != null && !Bounds.Contains(module.Right.Bounds))
                    Right = module.Right;
                //UpdateLocation();
            }
            AdjustSize();
        }
        
        public override void RemoveItem(IDrawable item)
        {
            if (_Left != null && item.Item != null && item.Item == ((Module)Item).GetLeftMostPart())
            {
                Left = null;
            }
            if (_Right != null && item.Item != null && item.Item == ((Module)Item).GetRightMostPart())
            {
                Right = null;
            }

            base.RemoveItem(item);
         
            if (!(Item is Module && ((Module)Item).IsPart))
            {
                Right = null;
                Left = null;
            }
            
        }
        */
        /// <summary>
        /// Get all the items in this module that have input connections to items outside the module
        /// </summary>
        [Description("The nodes that act as inputs for this module"), Category("Module Attributes")]
        public virtual List<IDrawable> Inputs
        {
            get
            {
                List<IDrawable> list = new List<IDrawable>();
                foreach (IDrawable id in _Drawables)
                {
                    if (id is IGlyph)
                    {
                        foreach (Connector c in ((IGlyph)id).IncomingConnections)
                        {
                            if (!_Drawables.Contains(c))
                            {
                                list.Add(id);
                                break;
                            }
                        }
                    }
                }
                return list;
            }
        }
        /// <summary>
        /// Get all the items in this module that have outputs connections to items outside the module
        /// </summary>
        [Description("The nodes that act as outputs for this module"), Category("Module Attributes")]
        public virtual List<IDrawable> Outputs
        {
            get
            {
                List<IDrawable> list = new List<IDrawable>();
                foreach (IDrawable id in _Drawables)
                {
                    if (id is IGlyph)
                    {
                        foreach (Connector c in ((IGlyph)id).OutgoingConnections)
                        {
                            if (!_Drawables.Contains(c))
                            {
                                list.Add(id);
                                break;
                            }
                        }
                    }
                }
                return list;
            }
        }

        /// <summary>
        /// Returns the list of items in this module that are exposed so that they 
        /// can interface with items outside this module
        /// </summary>
        [CategoryAttribute("Module Attributes"), Description("Whether or not to render this item on the screen")]        
        public List<IDrawable> ExposedItems
        {
            get
            {
                List<IDrawable> list = new List<IDrawable>();
                Module module = _Item as Module;
                if (_Item != null)
                {
                    foreach (Item item in module.ExposedItems)
                    {
                        if (item.Drawable != null && _Drawables.Contains(item.Drawable))
                        {
                            list.Add(item.Drawable);
                            if (item.Drawable.Alias != null && item.Drawable.Alias.Count > 0)
                                list.AddRange(item.Drawable.Alias);
                        }
                    }
                }
                return list;
            }
        }

        /// <summary>
        /// checks if the point is situated on the border of this compartment in non-encapsulate mode
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        public override bool Contains(PointF point)
        {
            if (Encapsulate) return base.Contains(point);

            RectangleF oTemp = Bounds;
            oTemp.Inflate(-3 * _dEdgeThickness, -3 * _dEdgeThickness);
            if (oTemp.Contains(point))
            {
                return false;
            }
            else
            {
                oTemp.Inflate(4*_dEdgeThickness,4* _dEdgeThickness);
                return oTemp.Contains(point);
            }
        }

        /// <summary>
        /// Checks that the given rectangle intersects with the border in non-encapsulate mode
        /// </summary>
        /// <param name="rect"></param>
        /// <returns></returns>
        public override bool IntersectsWith(RectangleF rect)
        {
            if (Encapsulate) return base.IntersectsWith(rect);

            RectangleF oTemp = Bounds;
            if (oTemp.Contains(rect))
            {
                return false;
            }
            else
            {
                return oTemp.IntersectsWith(rect);
            }
        }

/*
        /// <summary>
        /// Find the corner or the link at this point
        /// </summary>
        /// <param name="oPoint"></param>
        /// <returns></returns>
        public override Corner Where(PointF oPoint)
        {
            Corner corner = base.Where(oPoint);
            if (!(Item is Module) || !((Module)Item).IsPart) return corner;
            
            if (Item is Module && ((Module)Item).IsPart && corner == Corner.None)
            {
                RectangleF oBounds = Bounds;

                float szx = 50f, szy = 10f;
                RectangleF right = new RectangleF(oBounds.Right - szx, (oBounds.Top + 0.5f * oBounds.Height) - szy, 2 * szx, 2 * szy);
                RectangleF left = new RectangleF(oBounds.Left - szx, (oBounds.Top + 0.5f * oBounds.Height) - szy, 2 * szx, 2 * szy);

                if (left.Contains(oPoint))
                {
                    LinkClicked(Corner.ControlPoint);
                    return Corner.ControlPoint;
                }
                if (right.Contains(oPoint))
                {
                    LinkClicked(Corner.ControlPoint + 1);
                    return Corner.ControlPoint + 1;
                }

                return Corner.None;
            }
            else
                return corner;
        }
        
        /// <summary>
        /// The is called when a link (red bar) is clicked -- called by Where(PointF)
        /// </summary>
        /// <param name="oCorner"></param>
        protected virtual void LinkClicked(Corner oCorner)
        {
            float sz = 20;
            RectangleF oBounds = Bounds;

            if (oCorner == Corner.ControlPoint)
            {
                if (Left != null)
                {
                    Left = null;
                }
                else
                {
                    RectangleF left = new RectangleF(oBounds.Left - sz, (oBounds.Top + 0.5f * oBounds.Height) - sz, 2 * sz, 2 * sz);
                    List<IDrawable> hits = Program.Model.FindItems(left);
                    if (hits.Count > 0)
                        foreach (IDrawable id in hits)
                        {
                            if ((id is PartGlyph || id is ModuleGlyph) && id != this && id.Parent == Parent)
                            {
                                Left = id;
                                break;
                            }
                        }
                }
            }
            else
                if (oCorner == Corner.ControlPoint + 1)
                {
                    if (Right != null)
                    {
                        Right = null;
                    }
                    else
                    {
                        RectangleF right = new RectangleF(oBounds.Right - sz, (oBounds.Top + 0.5f * oBounds.Height) - sz, 2 * sz, 2 * sz);
                        List<IDrawable> hits = Program.Model.FindItems(right);
                        if (hits.Count > 0)
                            foreach (IDrawable id in hits)
                            {
                                if ((id is PartGlyph || id is ModuleGlyph) && id != this && id.Parent == Parent)
                                {
                                    Right = id;
                                    break;
                                }
                            }
                    }
                }
            //UpdateLocation();
        }
 
        /// <summary>
        /// This method adjusts all the attached PartGlyphs so that they are centered at the same y-axis
        /// </summary>
        public virtual void UpdateLocation()
        {
            IDrawable left = Left, right = Right;
            PointF location = Location;
            SizeF dimensions = Dimensions;

            while (left != null && left != this && !(Parent is ModuleGlyph && ((ModuleGlyph)Parent).Left == left))
            {
                while (left.Parent != null && left.Parent != Parent) left = left.Parent;

                if (((left.Parent == null && Parent == null) || left.Parent == Parent) && !left.IsSelected && (left.Location.X != location.X - left.Dimensions.Width - 5 || (left.Location.Y + left.Dimensions.Height / 2) != (location.Y + dimensions.Height / 2)))
                {
                    left.MoveTo(new PointF(location.X - left.Dimensions.Width - 5, location.Y + dimensions.Height / 2 - left.Dimensions.Height / 2));
                    //left.Location = (new PointF(location.X - left.Dimensions.Width - 5, location.Y + dimensions.Height / 2 - left.Dimensions.Height / 2));                    
                }
                location = left.Location;
                dimensions = left.Dimensions;
                if (left.Parent != null)
                    left.Parent.ChildChanged(left);
                if (left is PartGlyph)
                    left = ((PartGlyph)left).Left;
                if (left is ModuleGlyph)
                    left = ((ModuleGlyph)left).Left;
            }

            RectangleF bounds = Bounds;
            left = Left;
            right = Right;
            location = Location;
            dimensions = Dimensions;

            while (right != null && right != this && !(Parent is ModuleGlyph && ((ModuleGlyph)Parent).Right == right))
            {
                while (right.Parent != null && right.Parent != Parent) right = right.Parent;

                if (((Parent == null && right.Parent == null) || right.Parent == Parent) && !right.IsSelected && (location.X + dimensions.Width + 5 != right.Location.X || (location.Y + dimensions.Height / 2) != (right.Location.Y + right.Dimensions.Height / 2)))
                {
                    right.MoveTo(new PointF(location.X + dimensions.Width + 5, location.Y + dimensions.Height / 2 - right.Dimensions.Height / 2));
                    //right.Location = (new PointF(location.X + dimensions.Width + 5, location.Y + dimensions.Height / 2 - right.Dimensions.Height / 2));
                }
                location = right.Location;
                dimensions = right.Dimensions;
                bounds = right.Bounds;
                if (right.Parent != null)
                    right.Parent.ChildChanged(right);
                if (right is PartGlyph)
                    right = ((PartGlyph)right).Right;
                if (right is ModuleGlyph)
                    right = ((ModuleGlyph)right).Right;
            }
            Utility.Refresh();
        }
        */
        
        //[Serializable()]
        //private class Link : SpeciesGlyph
        //{
        //    public Link(PointF point) : base(point) { }
        //    public Link() : base() { }
        //    public Link(Link copy) : base(copy) { }

        //    public ModuleGlyph ParentModule;

        //    /*public override void MoveTo(PointF oNewPoint)
        //    {
        //        base.MoveTo(oNewPoint);
        //        if (ParentModule != null && InputConnections.Count > 0)
        //        {
        //            _Location = Utility.GetPointOnEdge(ParentModule.Bounds, InputConnections[0].Location, 0);
        //            _Location.X -= _Dimensions.Width / 2;
        //            _Location.Y -= _Dimensions.Height / 2;
        //        }

        //    }*/

        //    public override IDrawable Parent
        //    {
        //        get
        //        {
        //            return ParentModule;
        //        }
        //        set
        //        {
        //            _Parent = ParentModule;
        //        }
        //    }

        //    public override void RemoveConnector(Connector connector)
        //    {
        //        base.RemoveConnector(connector);
        //        if (connector.Parent != ParentModule)
        //        {
        //            foreach (Connector c in InputConnections)
        //            {
        //                c.Item = null;
        //            }
        //            foreach (Connector c in OutputConnections)
        //            {
        //                c.Item = null;
        //            }
        //        }
        //        else
        //        {
        //            Remove();
        //        }
        //    }

        //    public override void AddInputConnection(Connector connector)
        //    {
        //        base.AddInputConnection(connector);
        //        foreach (Connector c in OutputConnections)
        //        {
        //            c.Item = connector.Item;
        //        }
        //    }

        //    public override void AddOutputConnection(Connector connector)
        //    {
        //        base.AddOutputConnection(connector);
        //        foreach (Connector c in InputConnections)
        //        {
        //            c.Item = connector.Item;
        //        }
        //    }

        //}
        //protected virtual void MakeLink(IDrawable id)
        //{
        //    if (id is Connector && (id.Parent != this))
        //    {
        //        List<IDrawable> inputs = ((Connector)id).Inputs;
        //        List<IDrawable> outputs = ((Connector)id).Outputs;

        //        for (int i = 0; i < inputs.Count; ++i)
        //        {
        //            if (!(inputs[i] is Link) && inputs[i].Parent == this && inputs[i].Item is Node)
        //            {
        //                Link link = new Link(Utility.GetPointOnEdge(Bounds, id.Location, 0));
        //                link.Dimensions = new SizeF(4, 4);
        //                link.FillColorStart = link.FillColorEnd = Color.DarkBlue;
        //                link.Item = inputs[i].Item;
        //                link.Parent = this;
        //                ((Connector)id).SubstituteNode(inputs[i], link);
        //                BezierConnector bz = new BezierConnector(inputs[i],link);
        //                bz.SyncParent();

        //                bz.Item = ((Connector)id).Item;
        //                if (!Drawables.Contains(bz))
        //                    Drawables.Add(bz);
        //                if (!Drawables.Contains(link))
        //                    Drawables.Add(link);
        //                link.ShowLabel = false;
        //                link.ParentModule = this;

        //                Program.Model.Drawables.Add(bz);
        //                Program.Model.Drawables.Add(link);
        //            }
        //        }
        //        for (int i = 0; i < outputs.Count; ++i)
        //        {
        //            if (!(outputs[i] is Link) && outputs[i].Parent == this && outputs[i].Item is Node)
        //            {
        //                Link link = new Link(Utility.GetPointOnEdge(Bounds, id.Location, 0));
        //                link.Dimensions = new SizeF(4, 4);
        //                link.FillColorStart = link.FillColorEnd = Color.DarkBlue;
        //                link.Item = outputs[i].Item;
        //                link.Parent = this;
                        
        //                ((Connector)id).SubstituteNode(outputs[i], link);
        //                BezierConnector bz = new BezierConnector(link, outputs[i]);
        //                bz.SyncParent();

        //                bz.Item = ((Connector)id).Item;

        //                if (!Drawables.Contains(bz))
        //                    Drawables.Add(bz);
        //                if (!Drawables.Contains(link))
        //                    Drawables.Add(link);
        //                link.ShowLabel = false;
        //                link.ParentModule = this;

        //                Program.Model.Drawables.Add(bz);
        //                Program.Model.Drawables.Add(link);
        //            }
        //        }
        //    }
        //}

        /// <summary>
        /// Gets / Sets the rules for this module
        /// </summary>
        [CategoryAttribute("Module Attributes"), Description("The rules in this module")]
        public virtual Hashtable Rules
        {
            get
            {
                if (Item != null && Item is Module)
                    return ((Module)Item).LocalRules;
                else
                    return null;
            }
            set
            {
                if (Item != null && Item is Module)
                    ((Module)Item).LocalRules = value;
            }
        }

        protected bool _Encapsulate = false;
        /// <summary>
        /// Indicates whether or not to represent this module as a "black box"
        /// </summary>
        [CategoryAttribute("Module Attributes"), Description("Represent this module as a 'black box'")]
        public virtual bool Encapsulate
        {
            get
            {
                return _Encapsulate;
            }
            set
            {
                if (_Encapsulate == value) return; //nothing to do
                _Encapsulate = value;
                if (_Encapsulate)
                {
                    _dEdgeThickness /= 5f;
                    List<IDrawable> exposed = ExposedItems;
                    foreach (IDrawable id in Drawables)  //hide all non-IO items
                    {
                        if (!exposed.Contains(id))
                        {
                            id.IsVisible = false;
                        }
                    }
                }
                else   //show all hidden items
                {
                    _dEdgeThickness *= 5f;
                    foreach (IDrawable id in Drawables)
                    {
                        if (id.Parent == this)
                            id.IsVisible = true;
                    }
                    AdjustSize();
                }
            }
        }

        /// <summary>
        /// Sets all the contained items visibility IF not encapsulated
        /// </summary>
        public override bool IsVisible
        {
            get
            {
                return _IsVisible;
            }
            set
            {
                if (_Encapsulate)
                    _IsVisible = value;
                else
                    base.IsVisible = value;
            }
        }

        /// <summary>
        /// Same as container's hitby method, but does nothing if the module is in 
        /// encapsulation mode
        /// </summary>
        /// <param name="drawables"></param>
        /// <param name="moving"></param>
        public override void HitBy(ICollection<IDrawable> drawables, bool moving)
        {
            if (Encapsulate) return;
            base.HitBy(drawables, moving);
        }
        /// <summary>
        /// Same as container's hitby method, but does nothing if the module is in 
        /// encapsulation mode
        /// </summary>
        /// <param name="drawables"></param>
        /// <param name="moving"></param>
        public override void HitBy(IDrawable drawable, bool moving)
        {
            if (Encapsulate) return;
            base.HitBy(drawable, moving);
        }

        public override bool ChildChanged(IDrawable child)
        {
            if (Encapsulate)  //in encapsulate mode, nothing can leave
            {
                if (!Bounds.Contains(child.Bounds))
                    AdjustSize(child);
            }
            return base.ChildChanged(child);
        }

        public override void DoneModifying(Corner oCorner, bool modifyAnchoredItems)
        {
            if (Encapsulate)
            {
                AdjustSize();
            }
            base.DoneModifying(oCorner, modifyAnchoredItems);
        }
        /// <summary>
        /// Render this module -- renders differently based on whether or not
        /// encapsulate is on
        /// </summary>
        /// <param name="g"></param>
        public override void RenderGlyph(Graphics g)
        {
            if (_Encapsulate)
                RenderGlyphClosed(g);
            else
                RenderGlyphOpen(g);
        }

        /// <summary>
        /// render the module in encapsulation mode
        /// </summary>
        /// <param name="g"></param>
        void RenderGlyphClosed(Graphics g)
        {
            Pen oOutline = new Pen(_oEdgeColor, _dEdgeThickness);

            RectangleF oTemp = Bounds;

            Brush oFilled;
            if (_oFillColorStart == _oFillColorEnd || _Dimensions.Width < 2 || _Dimensions.Height < 2)
                oFilled = new SolidBrush(_oFillColorStart);
            else
                oFilled = new LinearGradientBrush(Location, new PointF(Location.X + Dimensions.Width, Location.Y + Dimensions.Height), _oFillColorStart, _oFillColorEnd);

            Pen glow = new Pen(Color.OrangeRed, 5);
            GraphicsPath path = new GraphicsPath();

            switch (Shape)
            {
                case SHAPE.Box:
                    {
                        g.FillRectangle(oFilled, oTemp);
                        oTemp.Inflate(-_dEdgeThickness, -_dEdgeThickness);
                        g.DrawRectangle(oOutline, oTemp.X,oTemp.Y,oTemp.Width,oTemp.Height);
                        if (_Glow)
                        {
                            oTemp.Inflate(2 * _dEdgeThickness, 2 * _dEdgeThickness);
                            g.DrawRectangle(glow, oTemp.X, oTemp.Y, oTemp.Width, oTemp.Height);
                        }
                        break;
                    }
                case SHAPE.Ellipse:
                    {
                        g.FillEllipse(oFilled, oTemp.X, oTemp.Y, oTemp.Width, oTemp.Height);
                        oTemp.Inflate(-_dEdgeThickness, -_dEdgeThickness);
                        g.DrawEllipse(oOutline, oTemp.X, oTemp.Y, oTemp.Width, oTemp.Height);
                        if (_Glow)
                        {
                            oTemp.Inflate(2 * _dEdgeThickness, 2 * _dEdgeThickness);
                            g.DrawEllipse(glow, oTemp.X, oTemp.Y, oTemp.Width, oTemp.Height);
                        }
                        break;
                    }
                case SHAPE.Octahedron:
                    {
                        float dOffset = Math.Min(Bounds.Width, Bounds.Height) * 0.10f;

                        GraphicsPath oPath = new GraphicsPath();

                        oPath.AddLine(oTemp.X, oTemp.Y + dOffset,
                                      oTemp.X + dOffset, oTemp.Y);
                        oPath.AddLine(oTemp.X + dOffset, oTemp.Y,
                                      oTemp.X + oTemp.Width - dOffset, oTemp.Y);
                        oPath.AddLine(oTemp.X + oTemp.Width - dOffset, oTemp.Y,
                                      oTemp.X + oTemp.Width, oTemp.Y + dOffset);
                        oPath.AddLine(oTemp.X + oTemp.Width, oTemp.Y + dOffset,
                                      oTemp.X + oTemp.Width, oTemp.Y + oTemp.Height - dOffset);
                        oPath.AddLine(oTemp.X + oTemp.Width, oTemp.Y + oTemp.Height - dOffset,
                                      oTemp.X + oTemp.Width - dOffset, oTemp.Y + oTemp.Height);
                        oPath.AddLine(oTemp.X + oTemp.Width - dOffset, oTemp.Y + oTemp.Height,
                                      oTemp.X + dOffset, oTemp.Y + oTemp.Height);
                        oPath.AddLine(oTemp.X + dOffset, oTemp.Y + oTemp.Height,
                                      oTemp.X, oTemp.Y + oTemp.Height - dOffset);
                        oPath.AddLine(oTemp.X, oTemp.Y + oTemp.Height - dOffset,
                                      oTemp.X, oTemp.Y + dOffset);

                        oPath.CloseFigure();
                        g.FillPath(oFilled, oPath);
                        g.DrawPath(oOutline, oPath);

                        if (_Glow)
                        {
                            GraphicsPath oPath2 = new GraphicsPath();

                            oPath2.AddLine(oTemp.X, oTemp.Y + dOffset,
                                          oTemp.X + dOffset, oTemp.Y);
                            oPath2.AddLine(oTemp.X + dOffset, oTemp.Y,
                                          oTemp.X + oTemp.Width - dOffset, oTemp.Y);
                            oPath2.AddLine(oTemp.X + oTemp.Width - dOffset, oTemp.Y,
                                          oTemp.X + oTemp.Width, oTemp.Y + dOffset);
                            oPath2.AddLine(oTemp.X + oTemp.Width, oTemp.Y + dOffset,
                                          oTemp.X + oTemp.Width, oTemp.Y + oTemp.Height - dOffset);
                            oPath2.AddLine(oTemp.X + oTemp.Width, oTemp.Y + oTemp.Height - dOffset,
                                          oTemp.X + oTemp.Width - dOffset, oTemp.Y + oTemp.Height);
                            oPath2.AddLine(oTemp.X + oTemp.Width - dOffset, oTemp.Y + oTemp.Height,
                                          oTemp.X + dOffset, oTemp.Y + oTemp.Height);
                            oPath2.AddLine(oTemp.X + dOffset, oTemp.Y + oTemp.Height,
                                          oTemp.X, oTemp.Y + oTemp.Height - dOffset);
                            oPath2.AddLine(oTemp.X, oTemp.Y + oTemp.Height - dOffset,
                                          oTemp.X, oTemp.Y + dOffset);

                            oPath2.CloseFigure();
                            //g.FillPath(oFilled, oPath2);
                            g.DrawPath(new Pen(Color.OrangeRed, 3), oPath2);
                        }

                        break;
                    }
                case SHAPE.RoundedRectangle:
                    {
                        GraphicsPath oPath = new GraphicsPath();

                        Utility.GenerateRoundedRectangle(oPath, oTemp);
                        RectangleF pathBounds = oPath.GetBounds();
                        Matrix oTransform = new Matrix();

                        oTransform.Scale(
                            oTemp.Width / pathBounds.Width,
                            oTemp.Height / pathBounds.Height, MatrixOrder.Prepend);
                        oTransform.Translate(oTemp.X - pathBounds.X * oTemp.Width / pathBounds.Width, oTemp.Y - pathBounds.Y * oTemp.Height / pathBounds.Height, MatrixOrder.Append);

                        oPath.Transform(oTransform);

                        path.AddPath(oPath, false);

                        g.FillPath(oFilled, path);

                        g.DrawPath(oOutline, path);

                        if (_Glow)
                        {
                            g.DrawPath(glow, oPath);
                        }

                        break;
                    }
            }

        }

        /// <summary>
        /// render the compartment with inside visible
        /// </summary>
        /// <param name="g"></param>
        void RenderGlyphOpen(Graphics g)
        {
            Pen oOutline = new Pen(_oEdgeColor, _dEdgeThickness);

            RectangleF oTemp = Bounds;

            Brush oFilled;
            if (_oFillColorStart == _oFillColorEnd)
                oFilled = new SolidBrush(_oFillColorStart);
            else
                oFilled = new LinearGradientBrush(Location, new PointF(Location.X + Dimensions.Width, Location.Y + Dimensions.Height), _oFillColorStart, _oFillColorEnd);

            //Brush glow = new LinearGradientBrush(Center, new PointF(Location.X - 200, Location.Y - 200), Color.Red, Program.Model.Background.StartColor);
            Pen glow = new Pen(Color.OrangeRed, 5);
            GraphicsPath path = new GraphicsPath();

            switch (Shape)
            {
                case SHAPE.Box:
                    {
                        //g.FillRectangle(oFilled, Bounds);
                        //oTemp.Inflate(-_dEdgeThickness, -_dEdgeThickness);
                        //path.AddRectangle(oTemp);
                        //oTemp.Inflate(-2 * _dEdgeThickness, -2 * _dEdgeThickness);
                        //path.AddRectangle(oTemp);
                        g.FillPath(oFilled, path);
                        g.DrawRectangle(oOutline, oTemp.X, oTemp.Y, oTemp.Width, oTemp.Height);
                        if (_Glow)
                        {
                            oTemp.Inflate(- _dEdgeThickness, - _dEdgeThickness);
                            g.DrawRectangle(glow, oTemp.X, oTemp.Y, oTemp.Width, oTemp.Height);
                        }
                        break;
                    }
                case SHAPE.Ellipse:
                    {
                        //g.FillEllipse(oFilled, oTemp.X, oTemp.Y, oTemp.Width, oTemp.Height);
                        //oTemp.Inflate(-_dEdgeThickness, -_dEdgeThickness);
                        //path.AddEllipse(oTemp);
                        //oTemp.Inflate(-2 * _dEdgeThickness, -2 * _dEdgeThickness);
                        //path.AddEllipse(oTemp);
                        //g.FillPath(oFilled, path);
                        g.DrawEllipse(oOutline, oTemp.X, oTemp.Y, oTemp.Width, oTemp.Height);
                        if (_Glow)
                        {
                            oTemp.Inflate(- _dEdgeThickness, - _dEdgeThickness);
                            g.DrawEllipse(glow, oTemp.X, oTemp.Y, oTemp.Width, oTemp.Height);
                        }
                        break;
                    }
                case SHAPE.Octahedron:
                    {
                        float dOffset = Math.Min(Bounds.Width, Bounds.Height) * 0.10f;

                        GraphicsPath oPath = new GraphicsPath();

                        oPath.AddLine(oTemp.X, oTemp.Y + dOffset,
                                      oTemp.X + dOffset, oTemp.Y);
                        oPath.AddLine(oTemp.X + dOffset, oTemp.Y,
                                      oTemp.X + oTemp.Width - dOffset, oTemp.Y);
                        oPath.AddLine(oTemp.X + oTemp.Width - dOffset, oTemp.Y,
                                      oTemp.X + oTemp.Width, oTemp.Y + dOffset);
                        oPath.AddLine(oTemp.X + oTemp.Width, oTemp.Y + dOffset,
                                      oTemp.X + oTemp.Width, oTemp.Y + oTemp.Height - dOffset);
                        oPath.AddLine(oTemp.X + oTemp.Width, oTemp.Y + oTemp.Height - dOffset,
                                      oTemp.X + oTemp.Width - dOffset, oTemp.Y + oTemp.Height);
                        oPath.AddLine(oTemp.X + oTemp.Width - dOffset, oTemp.Y + oTemp.Height,
                                      oTemp.X + dOffset, oTemp.Y + oTemp.Height);
                        oPath.AddLine(oTemp.X + dOffset, oTemp.Y + oTemp.Height,
                                      oTemp.X, oTemp.Y + oTemp.Height - dOffset);
                        oPath.AddLine(oTemp.X, oTemp.Y + oTemp.Height - dOffset,
                                      oTemp.X, oTemp.Y + dOffset);

                        oPath.CloseFigure();
                        //g.FillPath(oFilled, oPath);
                        g.DrawPath(oOutline, oPath);

                        if (_Glow)
                        {
                            GraphicsPath oPath2 = new GraphicsPath();

                            oPath2.AddLine(oTemp.X, oTemp.Y + dOffset,
                                          oTemp.X + dOffset, oTemp.Y);
                            oPath2.AddLine(oTemp.X + dOffset, oTemp.Y,
                                          oTemp.X + oTemp.Width - dOffset, oTemp.Y);
                            oPath2.AddLine(oTemp.X + oTemp.Width - dOffset, oTemp.Y,
                                          oTemp.X + oTemp.Width, oTemp.Y + dOffset);
                            oPath2.AddLine(oTemp.X + oTemp.Width, oTemp.Y + dOffset,
                                          oTemp.X + oTemp.Width, oTemp.Y + oTemp.Height - dOffset);
                            oPath2.AddLine(oTemp.X + oTemp.Width, oTemp.Y + oTemp.Height - dOffset,
                                          oTemp.X + oTemp.Width - dOffset, oTemp.Y + oTemp.Height);
                            oPath2.AddLine(oTemp.X + oTemp.Width - dOffset, oTemp.Y + oTemp.Height,
                                          oTemp.X + dOffset, oTemp.Y + oTemp.Height);
                            oPath2.AddLine(oTemp.X + dOffset, oTemp.Y + oTemp.Height,
                                          oTemp.X, oTemp.Y + oTemp.Height - dOffset);
                            oPath2.AddLine(oTemp.X, oTemp.Y + oTemp.Height - dOffset,
                                          oTemp.X, oTemp.Y + dOffset);

                            oPath2.CloseFigure();
                            //g.FillPath(oFilled, oPath2);
                            g.DrawPath(new Pen(Color.OrangeRed, 3), oPath2);
                        }

                        break;
                    }
                case SHAPE.RoundedRectangle:
                    {
                        GraphicsPath oPath = new GraphicsPath();

                        Utility.GenerateRoundedRectangle(oPath, oTemp);
                        RectangleF pathBounds = oPath.GetBounds();
                        Matrix oTransform = new Matrix();

                        oTransform.Scale(
                            oTemp.Width / pathBounds.Width,
                            oTemp.Height / pathBounds.Height, MatrixOrder.Prepend);
                        oTransform.Translate(oTemp.X - pathBounds.X * oTemp.Width / pathBounds.Width, oTemp.Y - pathBounds.Y * oTemp.Height / pathBounds.Height, MatrixOrder.Append);

                        oPath.Transform(oTransform);
                        
                        g.DrawPath(oOutline, oPath);

                        //path.AddPath(oPath, false);
                        if (_Glow)
                        {
                            oTemp.Inflate(-_dEdgeThickness, -_dEdgeThickness);

                            oPath = new GraphicsPath();
                            Utility.GenerateRoundedRectangle(oPath, oTemp);
                            pathBounds = oPath.GetBounds();
                            oTransform = new Matrix();
                            oTransform.Scale(
                                oTemp.Width / pathBounds.Width,
                                oTemp.Height / pathBounds.Height, MatrixOrder.Prepend);
                            oTransform.Translate(oTemp.X - pathBounds.X * oTemp.Width / pathBounds.Width, oTemp.Y - pathBounds.Y * oTemp.Height / pathBounds.Height, MatrixOrder.Append);

                            oPath.Transform(oTransform);

                            path.AddPath(oPath, false);


                            oPath.CloseFigure();

                            g.DrawPath(glow, oPath);
                        }

                        break;
                    }
            }

        }

        public override void ShowToolTip(ToolTip tooltip, Control pnlCanvas, PointF location)
        {
            tooltip.ToolTipTitle = "Module: ";
            tooltip.SetToolTip(pnlCanvas,
                String.Format(
                            "{2}ID:\t{0}"
                          + "{2}Num. items:\t{1}",
                Name,
                _Drawables.Count,
                "\r\n"));
        }

        public override void WriteXML(XmlWriter writer)
        {
            base.WriteXML(writer);

            writer.WriteStartElement("ModuleGlyph", "");

            writer.WriteStartElement("Encapsulate");
            writer.WriteValue(_Encapsulate);
            writer.WriteEndElement();

            writer.WriteEndElement();

        }
        
        public override bool ReadXML(System.Xml.XmlReader reader)
        {
            bool read = base.ReadXML(reader);
            read = reader.Read();

            if (!read) return false;

            if (reader.NodeType == XmlNodeType.Element && reader.Name == "ModuleGlyph")
            {
                read = reader.Read();
                while (read)
                {
                    if (reader.NodeType == XmlNodeType.EndElement)
                        break;

                    if (reader.NodeType == XmlNodeType.Element)
                    {
                        switch (reader.Name)
                        {
                            case "Encapsulate":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                    _Encapsulate = reader.ReadContentAsBoolean();
                                break;
                            default:
                                while (reader.NodeType != XmlNodeType.EndElement)
                                    read = reader.Read();
                                break;
                        }
                    }

                    if (!read || (reader.NodeType == XmlNodeType.EndElement && reader.Name == "ModuleGlyph")) break;

                    if (reader.NodeType != XmlNodeType.Element)
                        read = reader.Read();
                }
                return true;
            }
            return false;

        }
        
    }
}
